#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Unit tests for filesystem.py
import cStringIO
import filesystem
import generic_mock
import os
import tempfile
import unittest
import sys
import optparse

class TestFileSystemAF(unittest.TestCase):
    
    def testBasicFactory(self):
        fs = filesystem.FileSystemAF('mockfs')
        self.assertTrue(isinstance(fs, filesystem.MockFileSystem))

        fs = filesystem.FileSystemAF('realfs')
        self.assertTrue(isinstance(fs, filesystem.RealFileSystem))

        fs = filesystem.FileSystemAF('s3fs')
        self.assertTrue(isinstance(fs, filesystem.S3FileSystem))

        self.assertRaises(ValueError, filesystem.FileSystemAF,
                          'nonefs')
        
    def testRealFSBasedir(self):
        fs = filesystem.FileSystemAF('realfs', '/basedir')
        self.assertTrue(isinstance(fs, filesystem.RealFileSystem))
        self.assertEqual(fs.GetBaseDir(), '/basedir')

        fs = filesystem.FileSystemAF('realfs', basedir='/basedir')
        self.assertTrue(isinstance(fs, filesystem.RealFileSystem))
        self.assertEqual(fs.GetBaseDir(), '/basedir')

    def testS3FSAccessKey(self):
        fs = filesystem.FileSystemAF('s3fs', 'ID', 'KEY', 'BUCKET')
        self.assertTrue(isinstance(fs, filesystem.S3FileSystem))
        self.assertEqual(fs.access_id, 'ID')
        self.assertEqual(fs.access_key, 'KEY')
        self.assertEqual(fs.bucket_name, 'BUCKET')

        fs = filesystem.FileSystemAF('s3fs', access_id = 'ID', 
                                     access_key = 'KEY',
                                     bucket_name = 'BUCKET')
        self.assertTrue(isinstance(fs, filesystem.S3FileSystem))
        self.assertEqual(fs.access_id, 'ID')
        self.assertEqual(fs.access_key, 'KEY')
        self.assertEqual(fs.bucket_name, 'BUCKET')

class TestMockFileSystem(unittest.TestCase):
    
    def setUp(self):
        self.fs_ = filesystem.MockFileSystem()
    
    def testConstructor(self):
        self.assertEqual(self.fs_.files_, {})

    def testOpenFile(self):
        f = self.fs_.OpenFile('filename', 'w')
        self.assert_('filename' in self.fs_.files_)
        self.assertEqual(self.fs_.files_['filename']['mode'], 'w')
        f.write('TEST')
        f.close()
        self.assertEqual(self.fs_.files_['filename']['content'],
                         'TEST')
        f = self.fs_.OpenFile('filename', 'r')
        self.assertEqual(f.read(), 'TEST')
        f.close()

    def testMultipleFiles(self):
        f = self.fs_.OpenFile('filename', 'w')
        self.assertTrue('filename' in self.fs_.files_)
        self.assertEqual(self.fs_.files_['filename']['mode'], 'w')
        f.write('TEST')
        f2 = self.fs_.OpenFile('filename2', 'w')
        self.assertTrue('filename' in self.fs_.files_)
        self.assertTrue('filename2' in self.fs_.files_)
        f.close()
        f2.close()
        self.assertEqual(self.fs_.files_['filename']['mode'], 'w')
        self.assertEqual(self.fs_.files_['filename2']['mode'], 'w')

    def testReOpenFile(self):
        f = self.fs_.OpenFile('filename', 'w')
        self.assertTrue('filename' in self.fs_.files_)
        self.assertEqual(self.fs_.files_['filename']['mode'], 'w')
        f.write('TEST')
        f.close()
        self.assertEqual(self.fs_.files_['filename']['content'],
                         'TEST')
        f = self.fs_.OpenFile('filename', 'w')
        self.assertEqual(f.read(), 'TEST')
        f.write('TESTING AGAIN')
        self.assertEqual(self.fs_.files_['filename']['content'],
                         'TEST')
        f.close()
        self.assertEqual(self.fs_.files_['filename']['content'],
                         'TESTTESTING AGAIN')

    def testDeleteFile(self):
        f = self.fs_.OpenFile('filename', 'w')
        f.close()
        self.fs_.DeleteFile('filename')
        self.assertTrue('filename' not in self.fs_.files_)

class TestS3FileSystem(unittest.TestCase):
        
    def setUp(self):
        global global_access_id
        global global_access_key
        global global_bucket
        try:
            self.access_id_ = global_access_id
        except:
            global_access_id = raw_input('\nAccess ID for S3: ')
            self.access_id_ = global_access_id
        
        try:
            self.access_key_ = global_access_key
        except:
            global_access_key = raw_input('\nAccess Key for S3: ')
            self.access_key_ = global_access_key  

        try:
            self.bucket_ = global_bucket
        except:
            global_bucket = raw_input('\nBucket name for S3: ')
            self.bucket_ = global_bucket  

            
        self.fs_ = filesystem.S3FileSystem(access_id=self.access_id_,
                                           access_key=self.access_key_,
                                           bucket_name=self.bucket_)

    def testConstructor(self):
        self.assertEqual(self.fs_.access_id_, self.access_id_)
        self.assertEqual(self.fs_.access_key_, self.access_key_)
        self.assertEqual(self.fs_.bucket_name_, self.bucket_)
        self.assertEqual(self.fs_.conn_, None)
        self.assertEqual(self.fs_.bucket_, None)

    def testProperties(self):
        # This also test if the properties invalidate the 
        # connection (which will force a new connection)
        self.fs_.conn_ = 'test'
        self.fs_.access_id = 'access_id'
        self.assertEqual(self.fs_.conn_, None)
        self.fs_.conn_ = 'test'
        self.fs_.access_key = 'access_key'
        self.assertEqual(self.fs_.conn_, None)
        self.fs_.conn_ = 'test'
        self.fs_.bucket_name = 'bucket'
        self.assertEqual(self.fs_.conn_, None)
        self.assertEqual(self.fs_.access_id_, 'access_id')
        self.assertEqual(self.fs_.access_key_, 'access_key')
        self.assertEqual(self.fs_.bucket_name_, 'bucket')
        self.assertEqual(self.fs_.access_id, 'access_id')
        self.assertEqual(self.fs_.access_key, 'access_key')
        self.assertEqual(self.fs_.bucket_name, 'bucket')

    def testCheckConn(self):
        self.fs_.check_conn_()
        self.assertNotEqual(type(self.fs_.conn_), None)
        self.assertNotEqual(type(self.fs_.bucket_), None)

    def testOpenFile(self):
        f = self.fs_.OpenFile('filename', 'w')
        f.write('TEST')
        f.close()
        self.assertEqual(self.fs_.bucket_.get_key('filename').
                         get_contents_as_string(), 'TEST')
        f = self.fs_.OpenFile('filename', 'r')
        self.assertEqual(f.read(), 'TEST')
        f.close()
        self.fs_.bucket_.delete_key('filename')

    def testMultipleFiles(self):
        f = self.fs_.OpenFile('filename', 'w')
        f2 = self.fs_.OpenFile('filename2', 'w')        
        f.write('TEST')
        f2.write('TEST2')
        f.close()
        f2.close()
        self.assertEqual(self.fs_.bucket_.get_key('filename').
                         get_contents_as_string(), 'TEST')
        self.assertEqual(self.fs_.bucket_.get_key('filename2').
                         get_contents_as_string(), 'TEST2')
        self.fs_.bucket_.delete_key('filename')
        self.fs_.bucket_.delete_key('filename2')

    def testReOpenFile(self):
        f = self.fs_.OpenFile('filename', 'w')
        f.write('TEST')
        f.close()
        self.assertEqual(self.fs_.bucket_.get_key('filename').
                         get_contents_as_string(), 'TEST')
        f = self.fs_.OpenFile('filename', 'w')
        self.assertEqual(f.read(), 'TEST')
        self.assertEqual(self.fs_.bucket_.get_key('filename').
                         get_contents_as_string(), 'TEST')
        f.write('TESTING AGAIN')
        self.assertEqual(self.fs_.bucket_.get_key('filename').
                         get_contents_as_string(), 'TEST')
        f.close()
        self.assertEqual(self.fs_.bucket_.get_key('filename').
                         get_contents_as_string(), 'TESTTESTING AGAIN')

    def testDeleteFile(self):
        f = self.fs_.OpenFile('filename', 'w')
        f.close()
        self.fs_.DeleteFile('filename')
        self.assertFalse(self.fs_.bucket_.get_key('filename'))


class TestRealFileSystem(unittest.TestCase):
    
    def setUp(self):
        self.fs_ = filesystem.RealFileSystem()
        self.tempdir_ = tempfile.mkdtemp()
        self.fs_.SetBaseDir(self.tempdir_)
    
    def tearDown(self):
        for filename in os.listdir(self.tempdir_):
            fullname = '%s/%s' % (self.tempdir_, filename)
            os.remove(fullname)
        os.rmdir(self.tempdir_)

    def testConstructor(self):
        self.assertEqual(self.fs_.basedir_, self.tempdir_)

    def testSetBaseDir(self):
        self.fs_.SetBaseDir('basedir')
        self.assertEqual(self.fs_.basedir_, 'basedir')

        self.fs_.SetBaseDir('/basedir/')
        self.assertEqual(self.fs_.basedir_, '/basedir')

    def testGetBaseDir(self):
        self.fs_.basedir_ = 'basedir'
        self.assertEqual(self.fs_.GetBaseDir(), 'basedir')
        
    def testOpenFile(self):
        f = self.fs_.OpenFile('filename', 'w')
        f.write('TEST')
        f.close()
        f = open('%s/filename' % (self.tempdir_), 'r')
        self.assertEqual(f.read(), 'TEST')
        f.close()
        f = self.fs_.OpenFile('filename', 'r')
        self.assertEqual(f.read(), 'TEST')
        f.close()
    
    def testDeleteFile(self):
        f = self.fs_.OpenFile('filename', 'w')
        f.write('TEST')
        f.close()
        self.fs_.DeleteFile('filename')

if __name__ == '__main__':
    global global_access_id
    global global_access_key
    
    parser = optparse.OptionParser()
    parser.add_option('--access_id', dest='access_id', default='', 
                      action='store',
                      help='Access ID for S3 File system unit tests')
    parser.add_option('--access_key', dest='access_key', default='',
                      action='store',
                      help='Access Key for S3 File system unit tests')
    parser.add_option('--bucket', dest='bucket', default='',
                      action='store',
                      help='Bucket name for S3 File system unit tests')

    (options, args) = parser.parse_args()
    global_access_id = options.access_id
    global_access_key = options.access_key
    global_bucket = options.bucket

    if (not options.access_id or 
        not options.access_key or 
        not options.bucket) and (
        len(args) == 0 or 
        'TestS3FileSystem' in [i.split('.')[0] for i in args]): 
        print ('Some arguments are required in order to run S3 '
               'test case properly. Please provide the arguments below')
        if not options.access_id:
            global_access_id = raw_input('Access ID for S3: ')
        if not options.access_key:
            global_access_key = raw_input('Access Key for S3: ')
        if not options.bucket:
            global_access_key = raw_input('Bucket name for S3: ')
    unittest.main(argv=[sys.argv[0]] + args)
